package com.ypy.flexiplug.plugin.prometheus.parse;


import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ypy.flexiplug.plugin.prometheus.annotation.IResolver;
import com.ypy.flexiplug.plugin.prometheus.annotation.Resolver;
import com.ypy.flexiplug.plugin.prometheus.annotation.Transfer;
import org.springframework.util.StringUtils;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;


public class HandlerJsonFactory {
    public static final String PARSE_METHOD = "parse";
    public static <T> BiFunction<JsonObject, Map<String,Object>, T> dynamicParseForDTO(Class<T> clz) {

        return (obj, val) -> {
            T var0 = null;
            try {
                var0 = clz.newInstance();
                Field[] fields = clz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    setField(var0, field, obj, val);
                }
                Class<?> superClass = clz.getSuperclass();
                if (superClass != null && !superClass.equals(Object.class)) {
                    Field[] superFields = superClass.getDeclaredFields();
                    for (Field field : superFields) {
                        field.setAccessible(true);
                        setField(var0, field, obj, val);
                    }
                }
                return (T) var0;
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (NoSuchMethodException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        };
    }

    private static void setField(Object var1, Field field, JsonObject obj, Map<String,Object> map) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Transfer transfer = field.getAnnotation(Transfer.class);
        Resolver resolver = field.getAnnotation(Resolver.class);
        Class<?> type = field.getType();
        String target;
        if (null == transfer) {
            target = field.getName();
        } else {
            String from = transfer.from().trim();
            if (StringUtils.isEmpty(from)) {
                throw new RuntimeException("@Transfer annotation properties(from) must not empty.");
            } else {
                target = from;
            }
        }
        boolean isResolved = false;
        IResolver resolverObj = null;
        Method m = null;
        Object output = null;
        if (resolver != null) {
            String method = resolver.method().trim();
            if (StringUtils.isEmpty(method)) {
                method = PARSE_METHOD;
            }
            Class<? extends IResolver> clz = resolver.resolver();
            resolverObj = clz.newInstance();
            m = clz.getMethod(method, Object.class);
            isResolved = true;
        }

        boolean isValue = field.getName().equals("value") || field.getName().equals("values");
        if (isValue) {
            Type genericType = field.getGenericType();
            if (genericType!= null && genericType instanceof ParameterizedType) {
                Type[] typeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
                if (typeArguments!= null && typeArguments.length > 0) {
                    type = (Class<?>)typeArguments[0];
                }
            }
        }

        if (type == int.class || type == Integer.class) {
            if (isValue) {
                boolean isRange = (boolean)map.get("isRange");
                JsonArray ja1 = (JsonArray) map.get("value");
                if (isRange) {
                    List<Integer> values = new ArrayList<>();
                    for (JsonElement je : ja1) {
                        values.add(je.getAsJsonArray().get(1).getAsInt());
                    }
                    field.set(var1, values);
                }else {
                    field.set(var1, Integer.parseInt(ja1.get(1).getAsString()));
                }
            } else {
                field.set(var1, computeIfParse(type,m,resolverObj,obj.get(target).getAsInt(),isResolved));
            }
        } else if (type == String.class) {
            if (isValue) {
                boolean isRange = (boolean)map.get("isRange");
                JsonArray ja1 = (JsonArray) map.get("value");
                if (isRange) {
                    List<String> values = new ArrayList<>();
                    for (JsonElement je : ja1) {
                        values.add(je.getAsJsonArray().get(1).getAsString());
                    }
                    field.set(var1, values);
                }else {
                    field.set(var1, ja1.get(1).getAsString());
                }
            } else {
                field.set(var1, computeIfParse(type,m,resolverObj,obj.get(target).getAsString(),isResolved));
            }
        } else if (type == boolean.class || type == Boolean.class) {
            if (isValue) {
                boolean isRange = (boolean)map.get("isRange");
                JsonArray ja1 = (JsonArray) map.get("value");
                if (isRange) {
                    List<Boolean> values = new ArrayList<>();
                    for (JsonElement je : ja1) {
                        values.add(je.getAsJsonArray().get(1).getAsBoolean());
                    }
                    field.set(var1, values);
                }else {
                    field.set(var1, Boolean.parseBoolean(ja1.get(1).getAsString()));
                }
            } else {
                field.set(var1, computeIfParse(type,m,resolverObj,obj.get(target).getAsBoolean(),isResolved));
            }
        } else if (type == long.class || type == Long.class) {
            if (isValue) {
                boolean isRange = (boolean)map.get("isRange");
                JsonArray ja1 = (JsonArray) map.get("value");
                if (isRange) {
                    List<Long> values = new ArrayList<>();
                    for (JsonElement je : ja1) {
                        values.add(je.getAsJsonArray().get(1).getAsLong());
                    }
                    field.set(var1, values);
                }else {
                    field.set(var1, Long.parseLong(ja1.get(1).getAsString()));
                }
            } else {
                field.set(var1, computeIfParse(type,m,resolverObj,obj.get(target).getAsLong(),isResolved));
            }
        } else if (type == float.class || type == Float.class) {
            if (isValue) {
                boolean isRange = (boolean)map.get("isRange");
                JsonArray ja1 = (JsonArray) map.get("value");
                if (isRange) {
                    List<Float> values = new ArrayList<>();
                    for (JsonElement je : ja1) {
                        values.add(je.getAsJsonArray().get(1).getAsFloat());
                    }
                    field.set(var1, values);
                }else {
                    field.set(var1, Float.parseFloat(ja1.get(1).getAsString()));
                }
            } else {
                field.set(var1, computeIfParse(type,m,resolverObj,obj.get(target).getAsFloat(),isResolved));
            }
        } else if (type == double.class || type == Double.class) {
            if (isValue) {
                boolean isRange = (boolean)map.get("isRange");
                JsonArray ja1 = (JsonArray) map.get("value");
                if (isRange) {
                    List<Double> values = new ArrayList<>();
                    for (JsonElement je : ja1) {
                        values.add(je.getAsJsonArray().get(1).getAsDouble());
                    }
                    field.set(var1, values);
                }else {
                    field.set(var1, Double.parseDouble(ja1.get(1).getAsString()));
                }
            } else {
                field.set(var1, computeIfParse(type,m,resolverObj,obj.get(target).getAsDouble(),isResolved));
            }
        } else {
            throw new RuntimeException("Not support the type " + type.getSimpleName() + " for @Transfer annotation !");
        }

    }

    private static Object computeIfParse(Class<?> type, Method m, IResolver resolver, Object originalVal, boolean isResolved) throws InvocationTargetException, IllegalAccessException {
        if (isResolved) {
            return type.cast(m.invoke(resolver, originalVal));
        } else {
            return originalVal;
        }
    }

}

